package cn.initcap.nio.tcp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * @author initcap
 * @date Created in 4/29/19 9:06 PM.
 */
public class Server {

    public static void main(String[] args) throws IOException {
        new Server().start();
    }

    /**
     * 启动方法
     */
    public void start() throws IOException {
        //1. 创建Selector
        Selector selector = Selector.open();

        //2. 通过ServerSocketChannel创建channel通道
        ServerSocketChannel channel = ServerSocketChannel.open();

        //3. 为channel通道绑定监听端口
        channel.bind(new InetSocketAddress(8899));

        //4. 设置channel为非阻塞模式
        channel.configureBlocking(false);

        //5. 将channel注册到selector上，监听连接事件
        channel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务器启动成功!");

        //6. 循环等待新接入的连接
        int j = 0;
        while (true) {
            j++;
            if (j == Integer.MAX_VALUE) {
                break;
            }
            // 获取可用的channel数量
            int readChannels = selector.select();

            if (readChannels == 0) {
                continue;
            }

            // 获取可用channel的集合
            Set<SelectionKey> selectionKeyList = selector.selectedKeys();

            Iterator iterator = selectionKeyList.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = (SelectionKey) iterator.next();

                // 移除Set中的当前selectionKey
                iterator.remove();

                //7. 根据就绪状态，调用对应方法处理业务逻辑

                // 如果是 接入事件
                if (selectionKey.isAcceptable()) {
                    acceptHandler(channel, selector);
                }

                // 如果是 可读事件
                if (selectionKey.isReadable()) {
                    readHandler(selectionKey, selector);
                }

            }
        }

    }

    /**
     * 处理接入事件
     */
    private void acceptHandler(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
        // 创建socketChannel
        SocketChannel socketChannel = serverSocketChannel.accept();

        // 将socketChannel设置为非阻塞工作模式
        socketChannel.configureBlocking(false);

        // 将channel注册到selector上，监听 可读事件
        socketChannel.register(selector, SelectionKey.OP_READ);

        // 回复客户端提示信息
        socketChannel.write(Charset.forName("utf-8").encode("你与聊天室其他人都不是朋友关系，请注意!"));
    }

    /**
     * 处理可读事件
     */
    private void readHandler(SelectionKey selectionKey, Selector selector) throws IOException {
        // 要从selectionKey中获取到已经就绪的channel
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

        // 创建buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        // 循环读取客户端的请求信息
        String request = "";
        while (socketChannel.read(byteBuffer) > 0) {
            // 切换为读模式
            byteBuffer.flip();

            // 读取buffer中内容
            request += Charset.forName("utf-8").decode(byteBuffer);
        }

        // 将channel再次注册到selector上，监听他的可读事件
        socketChannel.register(selector, SelectionKey.OP_READ);

        // 将客户端发送的请求信息广播给其他客户端
        if (request.length() > 0) {
            // 广播给其他客户端
            System.out.println(" ::" + request);
            broadCast(selector, socketChannel, request);
        }

    }

    /**
     * 广播给你其他客户端
     */
    private void broadCast(Selector selector, SocketChannel sourceChannel, String request) {
        // 获取到所有以接入的客户端channel
        Set<SelectionKey> selectionKeys = selector.keys();
        selectionKeys.forEach(e -> {
            Channel targetChannel = e.channel();

            // 剔除发消息的channel
            if (targetChannel instanceof SocketChannel && targetChannel != sourceChannel) {
                try {
                    // 将消息发送到targetChannel客户端
                    ((SocketChannel) targetChannel).write(Charset.forName("utf-8").encode(request));
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        });

        // 循环向所有channel广播信息

    }

}
